package com.git.hui.yinshua.biz.util.net;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Call;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientResponseException;
import org.springframework.web.client.RestTemplate;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * 请求工具类
 *
 * @author YiHui
 * @date 2023/04/23
 */
@Slf4j
public class HttpRequestHelper {
    public static final String CHROME_UA =
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36";

    private static final HttpHeaders JSON_HEADERS;
    private static RestTemplate restTemplate;
    private static OkHttpClient okHttpClient = new OkHttpClient();

    static {
        restTemplate = buildRestTemplate();
        JSON_HEADERS = new HttpHeaders();
        JSON_HEADERS.setContentType(MediaType.APPLICATION_JSON);
    }

    /**
     * build rest template
     *
     * @return
     */
    private static RestTemplate buildRestTemplate() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setConnectTimeout(15000);
        factory.setReadTimeout(15000);
        return new RestTemplate(factory);
    }


    /**
     * 文件上传
     *
     * @param url       上传url
     * @param paramName 参数名
     * @param fileName  上传的文件名
     * @param bytes     上传文件流
     * @param params    其他的请求参数
     * @return
     */
    public static String upload(String url, String paramName, String fileName, byte[] bytes, String... params) {
        //设置请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);

        //设置请求体，注意是LinkedMultiValueMap
        ByteArrayResource fileSystemResource = new ByteArrayResource(bytes) {
            @Override
            public String getFilename() {
                return fileName;
            }
        };
        MultiValueMap<String, Object> form = new LinkedMultiValueMap<>();
        // post的文件
        form.add(paramName, fileSystemResource);
        if (params.length > 0) {
            for (int i = 0; i < params.length; i += 2) {
                form.add(params[i], params[i + 1]);
            }
        }

        //用HttpEntity封装整个请求报文
        HttpEntity<MultiValueMap<String, Object>> files = new HttpEntity<>(form, headers);
        String threadName = Thread.currentThread().getName();
        HttpEntity<String> res = restTemplate.postForEntity(url, files, String.class);
        return res.getBody();
    }

    private static String getFilename(String fileName) {
        int index = fileName.lastIndexOf("/");
        if (index > 0) {
            return fileName.substring(index + 1);
        } else {
            return fileName;
        }
    }

    /**
     * 解决腾讯云非常蛋疼的上传文件提示表单异常问题
     *
     * @param url
     * @param fileName
     * @param bytes
     * @param cloudPath
     * @param sign
     * @param token
     * @param cosFileId
     * @return
     */
    public static String uploadTxCloudByOk(String url, String fileName, byte[] bytes, String cloudPath, String sign, String token, String cosFileId) {
        MultipartBody multipartBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("key", cloudPath)
                .addFormDataPart("Signature", sign)
                .addFormDataPart("x-cos-security-token", token)
                .addFormDataPart("x-cos-meta-fileid", cosFileId)
                .addFormDataPart("file", getFilename(fileName),
                        // 注意这里，MediaType 需要和实际上传的文件类型一致，否则会报 The body of your POST request is not well-formed multipart/form-data 异常
                        RequestBody.create(okhttp3.MediaType.parse("image/*"), bytes))
                .build();
        Request request = new Request.Builder().url(url)
                .post(multipartBody)
                .header("Content-Type", "multipart/form-data")
                .build();
        Call call = okHttpClient.newCall(request);
        try (Response res = call.execute()) {
            return res.body().string();
        } catch (Exception e) {
            log.error("上传文件异常: ", e);
            return null;
        }
    }


    public static String uploadTxCloud(String url, String fileName, byte[] bytes, String cloudPath, String sign, String token, String cosFileId) {
        //设置请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);

        //设置请求体，注意是LinkedMultiValueMap
        ByteArrayResource fileSystemResource = new ByteArrayResource(bytes) {
            @Override
            public String getFilename() {
                int index = fileName.lastIndexOf("/");
                if (index > 0) {
                    return fileName.substring(index + 1);
                } else {
                    return fileName;
                }
            }
        };
        MultiValueMap<String, Object> form = new LinkedMultiValueMap<>();
        // post的文件
        form.add("file", fileSystemResource);
        form.add("key", cloudPath);
        form.add("Signature", sign);
        form.add("x-cos-security-token", token);
        form.add("x-cos-meta-fileid", cosFileId);

        HttpEntity<MultiValueMap<String, Object>> files = new HttpEntity<>(form, headers);
        HttpEntity<String> res = restTemplate.postForEntity(url, files, String.class);
        return res.getBody();
    }

    /**
     * @param url
     * @param method
     * @param params
     * @param headers
     * @param responseClass
     * @param <R>
     * @return
     */
    public static <R> R fetchContentWithProxy(String url, HttpMethod method, Map<String, String> params,
                                              HttpHeaders headers, Class<R> responseClass) {
        R result = fetchContent(url, method, params, headers, responseClass, true);
        if (result == null) {
            return fetchContent(url, method, params, headers, responseClass, false);
        }

        return result;
    }

    /**
     * post 表单请求
     *
     * @param url
     * @param params
     * @param responseClass
     * @param <R>
     * @return
     */
    public static <R> R postJson(String url, Map<String, String> params, Class<R> responseClass) {
        return fetchContentWithoutProxy(url, HttpMethod.POST, params, JSON_HEADERS, responseClass);
    }

    public static <R> R postForm(String url, Map<String, String> params, Class<R> responseClass) {
        return fetchContentWithoutProxy(url, HttpMethod.POST, params, null, responseClass);
    }

    /**
     * @param url
     * @param method
     * @param params
     * @param headers
     * @param responseClass
     * @param <R>
     * @return
     */
    public static <R> R fetchContentWithoutProxy(String url, HttpMethod method, Map<String, String> params,
                                                 HttpHeaders headers, Class<R> responseClass) {
        if (headers == null) {
            headers = new HttpHeaders();
        }
        return fetchContent(url, method, params, headers, responseClass, false);
    }

    /**
     * fetch content
     *
     * @param url
     * @param method
     * @param params
     * @param headers
     * @param responseClass
     * @param useProxy
     * @param <R>
     * @return
     */
    private static <R> R fetchContent(String url, HttpMethod method,
                                      Map<String, String> params,
                                      HttpHeaders headers,
                                      Class<R> responseClass, boolean useProxy) {
        if (useProxy) {
            String host = "";
            try {
                host = new URL(url).getHost();
            } catch (MalformedURLException e) {
                log.error("Failed to parse url:{}", url);
            }
            ensureProxy(restTemplate, host);
        } else {
            ensureProxy(restTemplate, "");
        }

        return fetchContentInternal(restTemplate, url, method, params, headers, responseClass);
    }

    /**
     * ensure proxy
     *
     * @param restTemplate
     * @param host
     */
    private static void ensureProxy(RestTemplate restTemplate, String host) {
        SimpleClientHttpRequestFactory factory = (SimpleClientHttpRequestFactory) restTemplate.getRequestFactory();
        if (StringUtils.isBlank(host)) {
            factory.setProxy(null);
            return;
        }

        Optional.ofNullable(ProxyCenter.loadProxy(host)).ifPresent(factory::setProxy);
    }

    /**
     * fetch content
     *
     * @param restTemplate
     * @param url
     * @param method
     * @param params
     * @param headers
     * @param responseClass
     * @param <R>
     * @return
     */
    @SuppressWarnings("unchecked")
    private static <R> R fetchContentInternal(RestTemplate restTemplate, String url, HttpMethod method,
                                              Map<String, String> params, HttpHeaders headers, Class<R> responseClass) {
        ResponseEntity<R> responseEntity;
        try {
            SslUtils.ignoreSSL();
            if (method.equals(HttpMethod.GET)) {
                HttpEntity<?> entity = new HttpEntity<>(headers);
                responseEntity = restTemplate.exchange(url, method, entity, responseClass, params);
            } else {
                if (Objects.equals(headers.getContentType(), MediaType.APPLICATION_JSON)) {
                    // json post上传
                    HttpEntity<String> entity = new HttpEntity<>(JSONObject.toJSONString(params), headers);
                    responseEntity = restTemplate.exchange(url, method, entity, responseClass);
                } else {
                    // 表单post请求
                    MultiValueMap<String, String> args = new LinkedMultiValueMap<>();
                    args.setAll(params);
                    HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(args, headers);
                    responseEntity = restTemplate.exchange(url, method, entity, responseClass);
                }
            }
        } catch (RestClientResponseException e) {
            String res = e.getResponseBodyAsString();
            if (String.class.isAssignableFrom(responseClass)) {
                return (R) res;
            } else if (JSONObject.class.isAssignableFrom(responseClass)) {
                return (R) JSONObject.parseObject(res);
            }
            return null;
        } catch (Exception e) {
            log.warn("Failed to fetch content, url:{}, params:{}, exception:{}", url, params, e.getMessage());
            return null;
        }

        return responseEntity.getBody();
    }
}